
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>

#include "tftlcdchar.h"
#include "tftlcd.h"


u8 page = 1;
u8 column = 10;
u8 firstWrite = 1;

/* tftlcd apis */
static ssize_t tftlcd_writeCommand(u8 cmd)
{
    spi_writeCommand(&tftlcddev, cmd);
    return 0;
}

#if 0
static ssize_t tftlcd_writeData(u8 data) 
{
    u8 buf = data;
    /*spi 写时序： */
    spi_writeData(&tftlcddev, &buf, 1);
    return 0;
}
#endif

static ssize_t tftlcd_setCursor(int x1, int y1, int x2, int y2) 
{
    u8 data[8] = {0};
    /* 设置光标位置, 起始点，结束点*/
    data[0] = x1 >> 8;
    data[1] = x1 & 0xFF;
    data[2] = x2 >> 8;
    data[3] = x2 & 0xFF;
    regmap_bulk_write(tftlcddev.regmap, 0x2A, data, 4);
    data[0] = y1 >> 8;
    data[1] = y1 & 0xFF;
    data[2] = y2 >> 8;
    data[3] = y2 & 0xFF;
    regmap_bulk_write(tftlcddev.regmap, 0x2B, data, 4);
    tftlcd_writeCommand(0x2C);

    return 0;
}

static void tftlcd_clear(u8 pix_h, u8 pix_l)
{
    int i;
    int j;
    u8 data[2] = {pix_h, pix_l};

    tftlcd_setCursor(0, 0, LCD_WIDTH, LCD_HEIGHT);
    for(i = 0; i < LCD_WIDTH; i ++)
    {
        for (j = 0; j < LCD_HEIGHT; j ++)
        {
            spi_writeData(&tftlcddev, data, 2);
        }
    }
}

static void tftlcd_fill(int x1, int y1, int x2, int y2, unsigned short color)
{
    int i;
    int j;

    u8 x_len = x2 - x1 + 1;
    u8 y_len = y2 - y1 + 1;
    u8 data[2] = {0};
    data[0] = color >> 8;
    data[1] = color & 0xFF;

    tftlcd_setCursor(x1, y1, x2, y2);
    for(i = 0; i < x_len; i ++)
    {
        for (j = 0; j < y_len; j ++)
        {
            spi_writeData(&tftlcddev, data, 2);
        }
    }
}

static int tftlcd_reset(struct spi_dev_node *tftlcddev)
{
    if (gpio_is_valid(tftlcddev->reset_gpio))
    {
#if 0
        // 申请复位IO，默认输出低电平
        ret = devm_gpio_request_one(tftlcddev,
                    tftlcddev->reset_gpio, GPIOF_OUT_INIT_LOW,
                    "st7735s reset");
        if (ret)
        {
            return ret;
        }
#endif

        gpio_set_value(tftlcddev->reset_gpio, 0); // 低电平
        msleep(5);
        gpio_set_value(tftlcddev->reset_gpio, 1); // 高电平，停止复位
        msleep(300);
    }

    return 0;
}

static int tftlcd_init(void)
{
    u8 data[32] = {0};

    tftlcd_reset(&tftlcddev);   // 复位
    tftlcd_writeCommand(0x11);  /* 退出休眠 */
    mdelay(200);

    data[0] = 0x00;
    regmap_bulk_write(tftlcddev.regmap, 0x36, data, 1);  // 寻址模式

#if ST7789
    data[0] = 0x05;
    regmap_bulk_write(tftlcddev.regmap, 0x3A, data, 1);  // 16位5-6-5彩色

    data[0] = 0x0C;
    data[1] = 0x0C;
    data[2] = 0x00;
    data[3] = 0x33;
    data[4] = 0x33;
    regmap_bulk_write(tftlcddev.regmap, 0xB2, data, 5);

    data[0] = 0x35;
    regmap_bulk_write(tftlcddev.regmap, 0xB7, data, 1);
    data[0] = 0x32;
    regmap_bulk_write(tftlcddev.regmap, 0xBB, data, 1);
    data[0] = 0x01;
    regmap_bulk_write(tftlcddev.regmap, 0xC2, data, 1);
    data[0] = 0x15;
    regmap_bulk_write(tftlcddev.regmap, 0xC3, data, 1);
    data[0] = 0x20;
    regmap_bulk_write(tftlcddev.regmap, 0xC4, data, 1);
    data[0] = 0x0F;
    regmap_bulk_write(tftlcddev.regmap, 0xC6, data, 1);
    data[0] = 0xA4;
    data[1] = 0xA1;
    regmap_bulk_write(tftlcddev.regmap, 0xD0, data, 2);
    data[0] = 0xD0;
    data[1] = 0x08;
    data[2] = 0x0E;
    data[3] = 0x09;
    data[4] = 0x09;
    data[5] = 0x05;
    data[6] = 0x31;
    data[7] = 0x33;
    data[8] = 0x48;
    data[9] = 0x17;
    data[10] = 0x14;
    data[11] = 0x15;
    data[12] = 0x31;
    data[13] = 0x34;
    regmap_bulk_write(tftlcddev.regmap, 0xE0, data, 14);
    data[0] = 0xD0;
    data[1] = 0x08;
    data[2] = 0x0E;
    data[3] = 0x09;
    data[4] = 0x09;
    data[5] = 0x15;
    data[6] = 0x31;
    data[7] = 0x33;
    data[8] = 0x48;
    data[9] = 0x17;
    data[10] = 0x14;
    data[11] = 0x15;
    data[12] = 0x31;
    data[13] = 0x34;
    regmap_bulk_write(tftlcddev.regmap, 0xE1, data, 14);

    tftlcd_writeCommand(0x21);  /* 打开反显 */
#else // ST7735
    data[0] = 0x05;
    data[1] = 0x3C;
    data[2] = 0x3C;
    regmap_bulk_write(tftlcddev.regmap, 0xB1, data, 3);
    data[0] = 0x05;
    data[1] = 0x3C;
    data[2] = 0x3C;
    regmap_bulk_write(tftlcddev.regmap, 0xB2, data, 3);
    data[0] = 0x05;
    data[1] = 0x3C;
    data[2] = 0x3C;
    data[3] = 0x05;
    data[4] = 0x3C;
    data[5] = 0x3C;
    regmap_bulk_write(tftlcddev.regmap, 0xB3, data, 6);
    data[0] = 0x03;
    regmap_bulk_write(tftlcddev.regmap, 0xB4, data, 1);

    data[0] = 0x28;
    data[1] = 0x08;
    data[2] = 0x04;
    regmap_bulk_write(tftlcddev.regmap, 0xC0, data, 3);
    data[0] = 0xC0;
    regmap_bulk_write(tftlcddev.regmap, 0xC1, data, 1);
    data[0] = 0x0D;
    data[1] = 0x00;
    regmap_bulk_write(tftlcddev.regmap, 0xC2, data, 2);
    data[0] = 0x8D;
    data[1] = 0x2A;
    regmap_bulk_write(tftlcddev.regmap, 0xC3, data, 2);
    data[0] = 0x8D;
    data[1] = 0xEE;
    regmap_bulk_write(tftlcddev.regmap, 0xC4, data, 2);

    data[0] = 0x1A;
    regmap_bulk_write(tftlcddev.regmap, 0xC5, data, 1);

    data[0] = 0x04;
    data[1] = 0x22;
    data[2] = 0x07;
    data[3] = 0x0A;
    data[4] = 0x2E;
    data[5] = 0x30;
    data[6] = 0x25;
    data[7] = 0x2A;
    data[8] = 0x28;
    data[9] = 0x26;
    data[10] = 0x2E;
    data[11] = 0x3A;
    data[12] = 0x00;
    data[13] = 0x01;
    data[14] = 0x03;
    data[15] = 0x13;
    regmap_bulk_write(tftlcddev.regmap, 0xE0, data, 16);
    data[0] = 0x04;
    data[1] = 0x16;
    data[2] = 0x06;
    data[3] = 0x0D;
    data[4] = 0x2D;
    data[5] = 0x26;
    data[6] = 0x23;
    data[7] = 0x27;
    data[8] = 0x27;
    data[9] = 0x25;
    data[10] = 0x2D;
    data[11] = 0x3B;
    data[12] = 0x00;
    data[13] = 0x01;
    data[14] = 0x04;
    data[15] = 0x13;
    regmap_bulk_write(tftlcddev.regmap, 0xE1, data, 16);

    data[0] = 0x05;
    regmap_bulk_write(tftlcddev.regmap, 0x3A, data, 1);  // 16位5-6-5彩色
#endif
    tftlcd_writeCommand(0x29);  /* 打开显示 */

    tftlcd_clear(0xFF, 0xFF);
    tftlcd_fill(50, 50, 80, 80, 0xF800);

#if 0
    data[0] = 0x00;
    data[1] = 0x85;
    data[2] = 0x85;
    data[3] = 0x52;
    regmap_bulk_write(tftlcddev.regmap, 0x04, data, 4);

    tftlcd_setCursor(0, 0, 20, 20);
    data[0] = 0xF8;
    data[1] = 0x00;
    for (u8 i = 0; i < 256; i ++)
        spi_writeData(&tftlcddev, data, 2);
#endif
    return 0;
}

/* 字符设备读写，用于上层调用 */
static int tftlcd_open(struct inode *inode, struct file *filp)
{
#ifdef ATOMIC
    if (!atomic_dec_and_test(&tftlcddev.lock))
    {
        atomic_inc(&tftlcddev.lock);
        return -EBUSY;
    }
#endif

#ifdef TIMER
    tftlcddev.period = 1000;
#endif

    filp->private_data = &tftlcddev; /* 设置私有数据 */
    tftlcd_init();
    return 0;
}

static ssize_t tftlcd_write(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    /* 第一次写字母需要清屏 */
    if (firstWrite == 1 || *buf == 0)
    {
#if 0
        tftlcd_clear(0xFF);
        tftlcd_clear(0x0);
#endif
        firstWrite = 0;
        page = 1;
        column = 10;

        if (*buf == 0)
        {
            return 0;
        }
    }

    if (*buf == 1)
    {
        page += 2;
        page %= 6;
        column = 10;
        return 0;
    }

#if 0
    tftlcd_writeChar(*buf);
#endif
    return 0;
}

static int tftlcd_release(struct inode *inode, struct file *filp)
{
    /*struct spi_dev *dev = filp->private_data;*/
#ifdef ATOMIC
    atomic_inc(&dev->lock);
#endif

    return 0;
}

static struct file_operations tftlcddev_fops = {
    .owner = THIS_MODULE,
    .open = tftlcd_open,
    .write = tftlcd_write,
    .release = tftlcd_release,
};

/* spi设备的具体功能 */
static int tftlcd_probe(struct spi_device *spi)
{
    int ret;
    printk(KERN_EMERG "\t match successed, initial spi charactor device\r\n");

    /* 注册字符设备驱动 */
    /* 1、创建设备号 */
    if (tftlcddev.major)
    {
        tftlcddev.devid = MKDEV(tftlcddev.major, 0);
        register_chrdev_region(tftlcddev.devid, DEV_CNT, CHRDEV_NAME);
    }
    else
    {
        alloc_chrdev_region(&tftlcddev.devid, 0, DEV_CNT, CHRDEV_NAME); /* 申请设备号 */
        tftlcddev.major = MAJOR(tftlcddev.devid); /* 获取主设备号 */
        tftlcddev.minor = MINOR(tftlcddev.devid); /* 获取次设备号 */
    }

    printk("tftlcddev major=%d,minor=%d\r\n",tftlcddev.major, tftlcddev.minor);

    /* 2、初始化 cdev */
    tftlcddev.cdev.owner = THIS_MODULE;
    cdev_init(&tftlcddev.cdev, &tftlcddev_fops);

    /* 3、添加一个 cdev */
    cdev_add(&tftlcddev.cdev, tftlcddev.devid, DEV_CNT);

    /* 4、创建类 */
    tftlcddev.class = class_create(THIS_MODULE, CHRDEV_NAME);
    if (IS_ERR(tftlcddev.class))
    {
        return PTR_ERR(tftlcddev.class);
    }

    /* 5、创建设备 */
    tftlcddev.device = device_create(tftlcddev.class, NULL, tftlcddev.devid, NULL, CHRDEV_NAME);
    if (IS_ERR(tftlcddev.device))
    {
        return PTR_ERR(tftlcddev.device);
    }

    /* regmap 初始化 */
    tftlcddev.regmap_config.reg_bits = 8;
    tftlcddev.regmap_config.val_bits = 8;
    tftlcddev.regmap_config.read_flag_mask = 0x80;
    tftlcddev.regmap = regmap_init_spi(spi, &tftlcddev.regmap_config);

    /* 1, 获取设备树中 cs片选信号 */
    tftlcddev.node = of_find_node_by_path("/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000");
    if(tftlcddev.node == NULL) 
    {
        printk("ecspi3 node not find!\r\n");
        return -EINVAL;
    }

    /* 2、 获取设备树中的 gpio属性，得到 CS片选所使用的 GPIO编号 */
    tftlcddev.cs_gpio = of_get_named_gpio(tftlcddev.node, "cs-gpio", 0);
    if(tftlcddev.cs_gpio < 0) 
    {
        printk("can't get cs-gpio");
        return -EINVAL;
    }
    tftlcddev.dc_gpio = of_get_named_gpio(tftlcddev.node, "dc-gpio", 0);
    if(tftlcddev.dc_gpio < 0) 
    {
        printk("can't get dc-gpio");
        return -EINVAL;
    }
    tftlcddev.reset_gpio = of_get_named_gpio(tftlcddev.node, "reset-gpio", 0);
    if(tftlcddev.reset_gpio < 0) 
    {
        printk("can't get reset-gpio");
        return -EINVAL;
    }

    /* 3、设置 GPIO1_IO20为输出，并且输出高电平, 当CSX为高电平时，
     * 串行接口初始化。在这种状态下，SCL时钟脉冲或SDA数据不起作用。
     * CSX的下降沿使能串行接口，并指示数据传输开始 */
    ret  = gpio_direction_output(tftlcddev.cs_gpio, 1);
    ret += gpio_direction_output(tftlcddev.dc_gpio, LCD_DATA);
    ret += gpio_direction_output(tftlcddev.reset_gpio, 1);
    if(ret < 0) 
    {
        printk("can't set gpio!\r\n");
        return -EINVAL;
    }

    /*初始化 spi_device */
    spi->mode = SPI_MODE_0; /*MODE0，CPOL=0，CPHA=0 */
    spi_setup(spi);

    tftlcddev.private_data = spi;

#ifdef TIMER
    /* 初始化定时器 */
    init_timer(&tftlcddev.timer);
    tftlcddev.timer.function = timer_handle;
#endif

    return 0;
}

static int tftlcd_remove(struct spi_device *spi)
{
#ifdef TIMER
    /* 删除定时器 */
    del_timer_sync(&tftlcddev.timer);
#endif

    /* 注销字符设备 */
    cdev_del(&tftlcddev.cdev);/* 删除 cdev */
    unregister_chrdev_region(tftlcddev.devid, DEV_CNT);

    device_destroy(tftlcddev.class, tftlcddev.devid);
    class_destroy(tftlcddev.class);
    gpio_free(tftlcddev.cs_gpio);

    regmap_exit(tftlcddev.regmap);

    return 0;
}

static int __init tftlcd_drv_init(void)
{
    int ret;
    printk("tftlcd drivers init\r\n");

    ret = spi_register_driver(&tftlcd_driver);

    return ret;
}

static void __exit tftlcd_drv_exit(void)
{
    printk("tftlcd drivers exit\r\n");

    spi_unregister_driver(&tftlcd_driver);
}

module_init(tftlcd_drv_init);
module_exit(tftlcd_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xue");


